package com.caregiver.watch.business.user.service.impl;

import com.caregiver.watch.business.notice.service.PushService;
import com.caregiver.watch.business.user.enums.UserStatusEnum;
import com.caregiver.watch.business.user.model.dto.LoginUser;
import com.caregiver.watch.business.user.model.po.User;
import com.caregiver.watch.business.user.model.vo.AccessTokenVo;
import com.caregiver.watch.business.user.service.LoginService;
import com.caregiver.watch.business.user.service.UserService;
import com.caregiver.watch.business.user.utils.SecurityUtils;
import com.caregiver.watch.common.constant.CacheConstants;
import com.caregiver.watch.common.enums.SendSmsCodeEnum;
import com.caregiver.watch.common.exception.BaseException;
import com.caregiver.watch.common.exception.business.BusinessException;
import com.caregiver.watch.common.redis.service.RedisService;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletRequest;
import java.util.Collection;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

import static com.caregiver.watch.common.constant.Constants.COLON;
import static com.caregiver.watch.common.constant.NumberConstants.ONE_LONG;
import static com.caregiver.watch.common.constant.NumberConstants.THIRTY_LONG;

/**
 * @Description: 登录服务
 * @Author 疆戟
 * @Date 2025/8/14 22:43
 * @Version 1.0
 */
@AllArgsConstructor
@Service
@Slf4j
public class LoginServiceImpl implements LoginService {

    private final RedisService redisService;
    private final UserService userService;
    private final TokenService tokenService;
    private final PushService pushService;


    @Override
    public AccessTokenVo userLogin(String username, String password) {
        if (StringUtils.isAnyBlank(username, password)) {
            throw new BusinessException("账号/密码须填写");
        }
        // 查询客户信息
        User user = userService.queryUserByUsername(username);
        if (Objects.isNull(user)) {
            throw new BusinessException("登录用户:" + username + " 不存在");
        }
        // 校验密码
        validate(user, password);
        return createToken(user);
    }

    /**
     * 登录账户密码错误次数缓存键名
     *
     * @param username 用户名
     * @return 缓存键key
     */
    private String getCacheKey(String username) {
        return CacheConstants.PWD_ERR_CNT_KEY + username;
    }

    /**
     * 验证密码
     * @param user
     * @param password
     */
    private void validate(User user, String password) {
        String username = user.getUsername();
        Long retryCount = redisService.increment(getCacheKey(username), ONE_LONG, THIRTY_LONG, TimeUnit.MINUTES);
        if (retryCount > Integer.valueOf(CacheConstants.PASSWORD_MAX_RETRY_COUNT).intValue()) {
            String errMsg = String.format("密码输入错误超过%s次，帐户锁定%s分钟", CacheConstants.PASSWORD_MAX_RETRY_COUNT, CacheConstants.PASSWORD_LOCK_TIME);
            throw new BusinessException(errMsg);
        }
        if (!SecurityUtils.matchesPassword(password, user.getPassword())) {
            throw new BusinessException("用户不存在/密码错误");
        } else {
            redisService.deleteObject(getCacheKey(username));
        }
    }

    /**
     * @description 客户短信登录
     * @param: phone
     * @param: code
     * @author YanDeChao
     * @date 2024/3/8
     */
    @Override
    public AccessTokenVo userLoginPhone(String phone, String code) {
        if (StringUtils.isAnyBlank(phone, code)) {
            throw new BusinessException("手机号/验证码须填写");
        }
        String oldCode = redisService.getCacheString(SendSmsCodeEnum.CUSTOMER_LOGIN.getKey() + COLON + phone);
        if (StringUtils.isBlank(oldCode)) {
            throw new BusinessException("验证码过期");
        } else if (!StringUtils.equals(oldCode, code)) {
            throw new BusinessException("验证码错误");
        }
        log.info("用户登录手机号: {}", phone);
        // 查询客户信息
        User user = userService.queryUserByPhone(phone);
        if (Objects.isNull(user)) {
            throw new BusinessException("登录用户:" + phone + " 不存在");
        }
        return createToken(user);
    }

    /**
     * 创建令牌
     *
     * @param user 用户信息
     * @return 令牌
     */
    private AccessTokenVo createToken(User user) {
        UserStatusEnum userStatus = UserStatusEnum.pares(user.getStatus());
        if (userStatus != UserStatusEnum.NORMAL) {
            throw new BusinessException("对不起，您的账号：" + user.getUsername() + " 已停用");
        }
        LoginUser loginUser = new LoginUser();
        loginUser.setUserId(user.getUserId());
        loginUser.setUsername(user.getUsername());
        return AccessTokenVo.build(tokenService.createToken(loginUser));
    }

    /**
     * 检查对象是否为空
     *
     * @param obj 要检查的数据(数据类型: String、Number、Boolean、Collection、Map、Object[])
     * @return true: 为空; false: 不为空 <li>String：值为 null、""、"0" 时返回 true <li>
     * Number：值为 null、0 时返回 true <li>Boolean：值为 null、false 时返回 true <li>
     * Collection：值为 null、size=0 时返回 true <li>Map：值为 null、size=0 时返回
     * true <li>Object[]：值为 null、length=0 时返回 true
     */
    public static boolean isEmpty(Object obj) {
        if (obj == null) {
            return true;
        } else if (obj instanceof String && ((obj.toString().trim().length() == 0) || ("null".equals(obj)))) {
            return true;
        } else if (obj instanceof Number && ((Number) obj).doubleValue() == 0) {
            return true;
        } else if (obj instanceof Boolean && !((Boolean) obj)) {
            return true;
        } else if (obj instanceof Collection && ((Collection<?>) obj).isEmpty()) {
            return true;
        } else if (obj instanceof Map && ((Map<?, ?>) obj).isEmpty()) {
            return true;
        } else return obj instanceof Object[] && ((Object[]) obj).length == 0;
    }

    @Override
    public Boolean logout(HttpServletRequest request) {
        String token = SecurityUtils.getToken(request);
        LoginUser loginUser = tokenService.delLoginUser(token);
        // 解绑极光推送ID
        if (Objects.nonNull(loginUser) && Objects.nonNull(loginUser.getUserId())) {
            pushService.unbindPushId(loginUser.getUserId());
        }
        return Boolean.TRUE;
    }
}
